package edu.hawaii.myisern.model;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static java.util.Arrays.asList;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import edu.hawaii.myisern.appconfig.jaxb.AppConfig;
import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.researchers.jaxb.Researcher;

/**
 * Implements a singleton IsernModel instance. The command classes manipulate this singleton in
 * response to user input. To prevent multi-threaded access from corrupting the stack, all
 * operations on the singleton IsernModel instance are synchronized.
 * 
 * @author Kevin English
 * 
 */

public class IsernManager {
  /** The static application configuration variable. **/
  private static AppConfig appConfig;
  /** The name of the application configuration xml file **/
  private static final String APP_CONFIG_XML_FILE = "appconfig.xml";
  /** The application data directory. Should be created by the user. */
  public static String defaultDirectory = ".myisern";
  /** The path to the xml files * */
  public static String xmlFilesPath = System.getProperty("user.home") + File.separatorChar
      + defaultDirectory;
  /** A static list of valid years for collaborations. **/ 
  private static List<String> validYears;
  /** The singleton instance of our IsernModel. **/  
  private static IsernModel model;
  /** A static list of orginzation types. **/
  private static List<String> organizationTypes = asList("Academic", "Government", "Classified",
      "Classified");
  static {
    validYears = new ArrayList<String>();
    
    for (int i = 1990 ; i <= 2010; i++ ) {
      validYears.add("" + i );
    }    
	  System.out.println("Static calling  loadApplication");
    loadApplication();
  }

  /**
   * Initialize the application setting from the appconfig.xml file. 
   * This includes loading researchers, organizations and collorations
   * from their respective xml files. The appconfig.xml is including with distribution
   * and tells the system which xml files to user. 
   */

  public static void loadApplication() {
    model = new IsernModel();
    boolean exists = (new File(xmlFilesPath)).exists();
    if (exists) {
      System.out.println("Directory Exists");
    }
    else {
      System.out.println("Directory DOES NOT Exists");
      boolean success = (new File(xmlFilesPath)).mkdir();
      if (!success) {
        System.out.println("Create Directory failed ");
      }
    }

    try {
      appConfig = loadAppConfig(xmlFilesPath + File.separatorChar + APP_CONFIG_XML_FILE);
      System.out.println("XML FILES found in " + xmlFilesPath);
      model.setPath(xmlFilesPath);
      model.setCollaborationsFile(appConfig.getCollaborationsXmlFilename());
      model.setOrganizationsFile(appConfig.getOrganizationsXmlFilename());
      model.setResearchersFile(appConfig.getResearchersXmlFilename());
      model.load();
    }
    catch (JAXBException e1) {
      e1.printStackTrace();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }

  /**
   * Load the application configuration from an xml file.
   * 
   * @param file The appconfig xml file to load.
   * @return An application configuration object.
   * @throws JAXBException When an error occurred loading the xml file.
   */
  public static synchronized AppConfig loadAppConfig(String file) throws JAXBException {
    AppConfig a = (AppConfig) JAXBContext.newInstance(AppConfig.class).createUnmarshaller()
        .unmarshal(new File(file));
    return a;
  }
  /**
   * Get the directory for the where application data files are stored.
   * 
   * @return An application configuration object.
   */
  public static String getApplicationDirectory() {
    return xmlFilesPath;
  }
  /**
   * Get the application configuration object.
   * 
   * @return An application configuration object.
   */
  public static synchronized AppConfig getAppConfig() {
    return appConfig;
  }
  /**
   * Gets the current list of researchers, wrapper for IsernModel method.
   * 
   * @return List<Researcher> A list of researchers
   */
  public synchronized List<Researcher> getResearchers() {
    return model.getResearcherList();
  }

  /**
   * Get researcher by id.
   * 
   * @param id The Id of the researcher to retrieve.
   * @return List<Researcher> A list of researchers
   */
  public synchronized Researcher getResearcher(Id id) {
    return model.getResearchers().get(id);
  }
  /**
   * Gets the current list of Collaboration, wrapper for  IsernModel method.
   * 
   * @return A list of Collaborations.
   */
  public synchronized List<Collaboration> getCollaborations() {
    return model.getCollaborationList();
  }
  /**
   * Gets the a collaboration by id.
   * 
   * @param id The Id of the collaboration to retrieve.
   * @return List<Researcher> A list of collaborations
   */
  public synchronized Collaboration getCollaboration(Id id) {
    return model.getCollaborations().get(id);
  }
  /**
   * Gets the current list of Organizations, wrapper for  IsernModel method.
   * 
   * @return A list of Organizations.
   */
  public synchronized List<Organization> getOrganizations() {
    return model.getOrganizationList();
  }
  /**
   * Gets the a organization by id.
   * 
   * @param id The Id of the researcher to retrieve.
   * @return List<Researcher> A list of organizations
   */
  public synchronized Organization getOrganization(Id id) {
    return model.getOrganizations().get(id);
  }

  /**
   * Save all data to xml file, wrapper for  IsernModel method.
   * 
   * @throws JAXBException If there's a jaxb exception.
   * @throws IOException If there's a IO exception.
   */
  public synchronized void save() throws IOException, JAXBException {
    model.save();
  }

  /**
   * Updates the organization with the given id, overwriting the data with data in the 
   * provided object.
   * 
   * @param id The Id for the given field.
   * @param organization The organization object with data to overwrite old organization.
   */
  public synchronized void updateOrganization(Id id, Organization organization) {
    Id newId = new Id(organization.getName());
    if (newId.toString().length() < 1) {
      return;
    }        
    if (!newId.equals(id)) {
      model.getOrganizations().remove(id);
    }
    model.getOrganizations().put(newId, organization);
  }

  /**
   * Creates a new organization.
   * 
   * @param organization The organization object.
   */
  public synchronized void createOrganization(Organization organization) {
    updateOrganization(null, organization);
  }

  /**
   * Saves the Organizations to xml file.
   * 
   * @throws JAXBException If there's a jaxb exception.
   * @throws IOException If there's a IO exception.
   */
  public synchronized void saveOrganizations() throws IOException, JAXBException {
    model.saveOrganizations();
  }

  /**
   * Updates the researcher with the given id, overwriting the data with data in the 
   * provided object.
   * 
   * @param id The Id for the given field.
   * @param researcher The researcher object.
   */
  public synchronized void updateResearcher(Id id, Researcher researcher) {
    Id newId = new Id(researcher.getName());
    if (newId.toString().length() < 1) {
      return;
    }        
    if (!newId.equals(id)) {
      model.getResearchers().remove(id);
    }
    model.getResearchers().put(newId, researcher);
  }

  /**
   * Create new researcher.
   * 
   * @param researcher The researcher object.
   */
  public synchronized void createResearcher(Researcher researcher) {
    updateResearcher(null, researcher);
  }

  /**
   * Update collaboration with the given id, overwriting the data with data in the 
   * provided object..
   * 
   * @param id The Id for the given field.
   * @param collaboration The collaboration object.
   */
  public synchronized void updateCollaboration(Id id, Collaboration collaboration) {
    Id newId = new Id(collaboration.getName());
    if (newId.toString().length() < 1) {
      return;
    }         
    if (!newId.equals(id)) {
      model.getCollaborations().remove(id);
    }
    model.getCollaborations().put(newId, collaboration);
  }

  /**
   * Create new collaboration.
   * 
   * @param collaboration The researcher object.
   */
  public synchronized void createCollaboration(Collaboration collaboration) {
    updateCollaboration(null, collaboration);
  }

  /**
   * Save the researchers to xml file.
   * 
   * @throws JAXBException If there's a jaxb exception.
   * @throws IOException If there's a IO exception.
   */
  public synchronized void saveResearchers() throws IOException, JAXBException {
    model.saveResearchers();
  }

  /**
   * Save the Collaborations to xml file.
   * 
   * @throws JAXBException If there's a jaxb exception.
   * @throws IOException If there's a IO exception.
   */
  public synchronized void saveCollaborations() throws IOException, JAXBException {
    model.saveCollaborations();
  }

  /**
   * Get the types of organizations.
   * 
   * @return A list of organization types.
   */
  public synchronized List<String> getOrganizationTypes() {
    return organizationTypes;
  }
  /**
   * Return a Set of all IDs in use.
   * 
   * @return a Set of IDs
   */
  public synchronized Set<Id> getIds() {
    return model.getIds();
  }
  /**
   * Get the year options.
   * 
   * @return A List of years (strings)
   */
  public List<String> getYearOptions() {
    return validYears;
  }  
}